﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using YasharEl.Infrastructure.EntityModel;
using YasharEl.Infrastructure.EntityModel.Specifications;
using System.Diagnostics.Contracts;

namespace YasharEl.Infrastructure.PersistenceModel
{
    /// <summary>
    /// Base interface for implement a "Repository Pattern", for
    /// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
    /// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
    /// </summary>
    /// <remarks>
    /// Indeed, one might think that IDbSet already a generic repository and therefore
    /// would not need this item. Using this interface allows us to ensure PI principle
    /// within our domain model
    /// </remarks>
    /// <typeparam name="TValueObject">Type of entity for this repository </typeparam>
    [ContractClass(typeof(GenericRepositoryContract<,>))]
    public interface IGenericRepository<TEntity, TIdentity>
        where TEntity : class, IGenericEntity<TIdentity>
    {
        void Insert(TEntity entity);

        void Update(TEntity entity);

        void Delete(TEntity entity);

        TEntity GetById(TIdentity id);

        IList<TEntity> GetAll();

        TSpecification Specify<TSpecification>()
            where TSpecification : ISpecification<TEntity, TIdentity>;

        IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> filter);
    }

    [ContractClassFor(typeof(IGenericRepository<,>))]
    internal abstract class GenericRepositoryContract<TEntity, TIdentity> : IGenericRepository<TEntity,TIdentity>
        where TEntity : class, IGenericEntity<TIdentity>
    {
        #region IGenericRepository<TEntity,TIdentity> Members

        public TEntity GetById(TIdentity id)
        {
            return default(TEntity);
        }

        public IList<TEntity> GetAll()
        {
            return new List<TEntity>();
        }

        public TSpecification Specify<TSpecification>() where TSpecification : ISpecification<TEntity, TIdentity>
        {
            return default(TSpecification);
        }

        public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> filter)
        {
            return new List<TEntity>();
        }

        public IEnumerable<TEntity> DinamicWhere(IList<Func<dynamic, dynamic>> dinamicQueries)
        {
            Contract.Requires<ArgumentNullException>(dinamicQueries != null, "dinamicQueries could not be null.");
            return new List<TEntity>();
        }

        public void Insert(TEntity entity)
        {
            Contract.Requires<ArgumentNullException>(entity != null, "entity could not be null.");
        }

        public void Update(TEntity entity)
        {
            Contract.Requires<ArgumentNullException>(entity != null, "entity could not be null.");
        }

        public void Delete(TEntity entity)
        {
            Contract.Requires<ArgumentNullException>(entity != null, "entity could not be null.");
        }

        public IEnumerable<TEntity> DynamicWhere(IList<Func<dynamic, dynamic>> dynamicQueries)
        {
            Contract.Requires<ArgumentNullException>(dynamicQueries != null, "dynamicQueries could not be null.");
            return new List<TEntity>();
        }

        #endregion
    }
}
